import re
import threading
import traceback

from django.conf import settings
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.utils.deprecation import MiddlewareMixin

from apps.base.models import Log
from apps.base.models import User, Rights

_thread_locals = threading.local()


def get_request():
    return getattr(_thread_locals, 'request', None)


class RbacMiddleware(MiddlewareMixin):
    def process_request(self, request):
        current_path = request.path_info

        """
        ★ 登陆认证 （登陆才能访问的url,白名单<无需登陆即可访问的url>除外）
          页面必须登陆后才能访问/通过session来进行登陆验证,可设置白名单
        """
        for white_path in settings.VALID_URL_LIST:
            if re.match(white_path, current_path):
                return None

        is_login = request.session.get(settings.LOGIN_SESSION_KEY, None)
        # 请求携带的cookies中没取到session信息
        if not is_login:
            return JsonResponse({'code': "401-1", 'msg': "用户未登录!", 'user': "游客"}, status=401)
            # return redirect('/login.html/')
        # (´･Д･)」session信息<用户登陆认证相关>所对应的用户未启用或已被软删除
        # 若不在中间件里来这么一手,只要该用户的session不过期,不主动退出登陆清除session信息,用户禁用对它来说不起作用!!
        if User.objects.filter(id=is_login).filter(Q(is_enabled=False) | Q(is_deleted=True)):
            user_name = User.objects.filter(id=is_login).first().nickname
            return JsonResponse({'code': "401-2", 'msg': "用户未启用或删除!", 'user': user_name}, status=401)
        # - 校验成功 给request.user赋值为当前登陆用户的id,便于后续动态菜单的渲染 -- 其实没必要,动态菜单那也可直接从request.session中取就好.
        #   只是说,这样搞的话,借鉴了drf的认证组件,认证成功时会返回(user,auth) 这样代码逻辑更清晰一点!! 而且在导航栏显示当前登陆用户是谁更方便!
        request.user = User.objects.filter(id=is_login).first()  # - 当前登陆用户对象
        # - 便于后续在主页右上角渲染当前登陆用户的头像
        if request.user.avatar:
            path = request.user.avatar
        else:
            path = settings.DEFAULT_AVATAR
        request.user.avatar = request.build_absolute_uri(path)
        request.auth = is_login  # - session信息,此处,它里面是登陆用户的id

        _thread_locals.request = request  # - 给当前线程绑定request对象,方便在信号日志中使用request,借此获取当前登陆用户!

        """
        ★ 权限验证 （拥有这个权限才能访问这个url）
          页面必须登陆后才能访问/通过session来进行登陆验证,可设置白名单(需要登录但无需权限的URL)
          - 超级管理员拥有所有权限(软删除的除外),无需权限验证
        """
        if request.user.is_super:
            return None
        permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        for white_path in settings.NO_PERMISSION_LIST:
            if re.match(white_path, current_path):  # eg: re.match('^/$','/')
                return None
        # - (´･Д･)」session信息<权限相关>所对应的权限未启用或已被软删除 （只要我禁用了该权限,给用户分配了也没用）
        if Rights.objects.filter(url=current_path).filter(Q(is_enabled=False) | Q(is_deleted=True)):
            return JsonResponse({'code': "403-1", 'msg': "权限已被禁用或删除!"}, status=403)
        if current_path not in permission_list:
            return JsonResponse({'code': "403-2", 'msg': "用户无权限访问此路由!"}, status=403)

        return None

    def process_response(self, request, response):
        if response.status_code == 401:
            return redirect('/login.html/')
        elif response.status_code == 403:
            return render(request, 'error/403.html', status=403)
        elif response.status_code == 404:
            return render(request, 'error/404.html', status=404)
        elif response.status_code != 200:
            return render(request, 'error/500.html', status=500)
        return response

    def process_exception(self, request, exception):
        """捕获了请求相关的异常并记录到日志表中"""
        url = request.path_info
        browser = request.META.get('HTTP_USER_AGENT', '')
        ip_address = request.META.get('REMOTE_ADDR')
        # 创建日志记录
        Log.objects.create(
            url=url,
            browser=browser,
            ip_address=ip_address,
            status=500,
            message=str(exception),
        )
